// =====================================================================================
// M8Pim: Provide a convenient way to manage personal information on Meizu M8.
// Copyright (C)  2009  tiger.lee  http://tigerlee.me
// 
// GNU General Public Licence:
// ---------------------------
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or any
// later version.
// 
// This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
// 
//      You should have received a copy of the GNU General Public License
//      along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// =====================================================================================

// =====================================================================================
//     Filename: PimData.cpp
//  Description: 
//
//       Create: 12/02/09 18:59:25
//       Author: tiger.lee
//        Email: i.m.tiger.lee(at)gmail.com
//      Website: http://tigerlee.me
//
//      Project: http://code.google.c/p/m8pim/
//   Bug Report: http://code.google.com/p/m8pim/issues/list
// =====================================================================================

#include	"PimData.h"

using namespace std;


//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimDataHandle::CPimDataHandle()
{
}

CPimDataHandle::~CPimDataHandle()
{
}


//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimDBHandle::CPimDBHandle(CPimString strContactDB, CPimString strSmsDB) :
	m_strContactDB(strContactDB),
	m_strSmsDB(strSmsDB),
	m_nContactDBOpenCount(0),
	m_nSmsDBOpenCount(0)
{
}

CPimDBHandle::~CPimDBHandle()
{
}

BOOL CPimDBHandle::OpenContactDB()
{
	if (m_nContactDBOpenCount == 0)
	{
		try
		{
			m_contactDB.open(m_strContactDB.c_str());
		}
		catch (CppSQLite3Exception e)
		{
			return FALSE;
		}
	}

	++m_nContactDBOpenCount;

	return TRUE;
}

BOOL CPimDBHandle::OpenSmsDB()
{
	if (m_nSmsDBOpenCount == 0)
	{
		try
		{
			m_smsDB.open(m_strSmsDB.c_str());
		}
		catch (CppSQLite3Exception e)
		{
			return FALSE;
		}
	}

	++m_nSmsDBOpenCount;

	return TRUE;
}

BOOL CPimDBHandle::CloseContactDB()
{
	if (m_nContactDBOpenCount == 1)
	{
		m_contactDB.close();
	}
	else if (m_nContactDBOpenCount > 1)
	{
		--m_nContactDBOpenCount;
	}
	else
	{
		// wrong close()
	}
	return TRUE;
}

BOOL CPimDBHandle::CloseSmsDB()
{
	if (m_nSmsDBOpenCount == 1)
	{
		m_smsDB.close();
	}
	else if (m_nSmsDBOpenCount > 1)
	{
		--m_nSmsDBOpenCount;
	}
	else
	{
		// wrong close()
	}
	return TRUE;
}


//----------------------------------------------------------------------
//  phone operations
//----------------------------------------------------------------------
BOOL CPimDBHandle::LoadPhones(CPimPhoneArray* pPhoneArray, int nContactId)
{
	BOOL nRet = TRUE;
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("select * from %s where record_id = %d"),
			TABLE_PHONE, nContactId);

	OpenContactDB();
	try
	{
		CppSQLite3Query Recordset = m_contactDB.execQuery(szSQL);
		while(!Recordset.eof())
		{
			PHONE* pPhone = new PHONE;

			pPhone->nId						= Recordset.getIntField(0);
			pPhone->nContactId				= Recordset.getIntField(1);
			pPhone->strNumber.assign		( Recordset.getStringField(2));
			pPhone->strNumberKey.assign		( Recordset.getStringField(3));
			pPhone->strPureNumber.assign	( Recordset.getStringField(4));
			pPhone->nLabel					= Recordset.getIntField(5);
			pPhone->nIsPrimary				= Recordset.getIntField(6);

			pPhoneArray->push_back(pPhone);
			Recordset.nextRow();
		}
		Recordset.finalize();
	}
	catch (CppSQLite3Exception e)
	{
		// TODO: need to clean resources after catch a exception.
		nRet = FALSE;
	}
	CloseContactDB();

	return nRet;
}

BOOL CPimDBHandle::CreatePhone(PHONE* pPhone)
{
	return TRUE;
}

BOOL CPimDBHandle::SearchPhone(PHONE* pPhone)
{
	return TRUE;
}

BOOL CPimDBHandle::UpdatePhone(PHONE* pPhone)
{
	TCHAR szSQL[512];
	_stprintf_s(szSQL, sizeof(szSQL), _T("update %s set \
				record_id  = %d,  \
				number     = '%s', \
				number_key = '%s', \
				pureNumber = '%s', \
				label      = %d, \
				isprimary  = %d where ROWID = %d"),
			TABLE_PHONE, 
			pPhone->nContactId,
			pPhone->strNumber.c_str(),
			pPhone->strNumberKey.c_str(),
			pPhone->strPureNumber.c_str(),
			pPhone->nLabel,
			pPhone->nIsPrimary, pPhone->nId);

	OpenContactDB();
	int nRet = m_contactDB.execDML(szSQL);
	CloseContactDB();

	return (nRet == 0) ? TRUE : FALSE;
}

BOOL CPimDBHandle::DeletePhone(PHONE* pPhone)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("delete from %s where ROWID = %d"),
			TABLE_PHONE, pPhone->nId);

	OpenContactDB();
	int nRet = m_contactDB.execDML(szSQL);
	CloseContactDB();

	return (nRet == 0) ? TRUE : FALSE;
}


//----------------------------------------------------------------------
//  contact operations
//----------------------------------------------------------------------
BOOL CPimDBHandle::LoadContacts(CPimContactMap& pContactMap)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("select * from %s"), TABLE_CONTACT);

	OpenContactDB();
	CppSQLite3Query Recordset = m_contactDB.execQuery(szSQL);
	while(!Recordset.eof())
	{
		CONTACT* 		pContact 	= new CONTACT;
		CPimPhoneArray*	pPhoneArray = new CPimPhoneArray;

		pContact->nId					= Recordset.getIntField(0);
		pContact->strFirstName.assign	( Recordset.getStringField(1));
		pContact->strLastName.assign	( Recordset.getStringField(2));
		pContact->strFullName.assign	( Recordset.getStringField(3));
		pContact->strOrg.assign			( Recordset.getStringField(4));
		pContact->strNote.assign		( Recordset.getStringField(5));
		pContact->strBirthday.assign	( Recordset.getStringField(6));
		pContact->strRingPath.assign	( Recordset.getStringField(7));
		pContact->nPrimaryPhone			= Recordset.getIntField(8);
		pContact->pPhoneArray 			= pPhoneArray;

		CPimContact* pPimContact = new CPimContact(pContact, this);

		LoadPhones(pContact->pPhoneArray, pContact->nId);

		pContactMap.insert(make_pair(pContact->nId, pPimContact));
		Recordset.nextRow();
	}
	Recordset.finalize();
	CloseContactDB();

	return TRUE;
}

BOOL CPimDBHandle::CreateContact(CONTACT* pContact)
{
	return TRUE;
}

BOOL CPimDBHandle::SearchContact(CONTACT* pContact)
{
	return TRUE;
}

BOOL CPimDBHandle::SearchContactsByFirstLatter(
		CPimStringArray& strFirstLetters,
		CPimIntArray& pContactIdArray)
{
	BOOL nRet = TRUE;
	TCHAR szSQL[512];
	CPimString strCondition(L"'fill'");
	CPimStringArrayIter iterLetter = strFirstLetters.begin();

	for (; iterLetter != strFirstLetters.end(); ++iterLetter)
	{
		// TODO: need optimize, instead of TCHAR array.
		strCondition += (CPimString(L", '") + *iterLetter + CPimString(L"'"));
	}

	_stprintf_s(szSQL, sizeof(szSQL), _T("select source from %s where \
				token in (%s) \
				order by isprimary"),
			TABLE_FIRST_LETTER, 
			strCondition.c_str());

	OpenContactDB();
	CppSQLite3Query Recordset = m_contactDB.execQuery(szSQL);
	while(!Recordset.eof())
	{
		int nId = Recordset.getIntField(0);
		pContactIdArray.push_back(nId);
		Recordset.nextRow();
	}
	Recordset.finalize();
	CloseContactDB();

	return nRet;
}

BOOL CPimDBHandle::UpdateContact(CONTACT* pContact)
{
	TCHAR szSQL[512];
	_stprintf_s(szSQL, sizeof(szSQL), _T("update %s set \
				First = '%s',  \
				Last  = '%s',  \
				Name  = '%s', \
				Organization = '%s', \
				Note     = %d, \
				Birthday = %d, \
				RingPath = %d, \
				PrimaryPhone = %d where ROWID = %d"),
			TABLE_CONTACT, 
			pContact->strFirstName,
			pContact->strLastName,
			pContact->strFullName,
			pContact->strOrg,
			pContact->strNote,
			pContact->strBirthday,
			pContact->strRingPath,
			pContact->nPrimaryPhone, pContact->nId);


	OpenContactDB();
	int nRet = m_contactDB.execDML(szSQL);
	CloseContactDB();

	CPimPhoneArrayIter iter = pContact->pPhoneArray->begin();
	for (; iter != pContact->pPhoneArray->end(); ++iter)
	{
		nRet = UpdatePhone(*iter);
	}

	return (nRet == 0) ? TRUE : FALSE;
}

BOOL CPimDBHandle::DeleteContact(CONTACT* pContact)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("delete from %s where ROWID = %d"),
			TABLE_CONTACT, pContact->nId);

	OpenContactDB();
	int nRet = m_contactDB.execDML(szSQL);
	CloseContactDB();

	return (nRet == 0) ? TRUE : FALSE;
}


//----------------------------------------------------------------------
//  call operations
//----------------------------------------------------------------------
BOOL CPimDBHandle::LoadCalls(CPimCallArray* pCallArray)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("select * from %s"), TABLE_CALL);

	OpenContactDB();
	CppSQLite3Query Recordset = m_contactDB.execQuery(szSQL);
	while(!Recordset.eof())
	{
		CALL* pCall = new CALL;

		pCall->nId					= Recordset.getIntField(0);
		pCall->strNumber.assign		( Recordset.getStringField(1));
		pCall->callTime				= Recordset.getIntField(2);
		pCall->nDuration			= Recordset.getIntField(3);
		pCall->nType				= Recordset.getIntField(4);
		pCall->strName.assign		( Recordset.getStringField(5));
		pCall->nContactId			= Recordset.getIntField(6);
		pCall->strPureNumber.assign	( Recordset.getStringField(7));

		pCallArray->push_back(pCall);
		Recordset.nextRow();
	}
	Recordset.finalize();
	CloseContactDB();

	return TRUE;
}

BOOL CPimDBHandle::CreateCall(CALL* pCall)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("insert into %s \
				(number, date, duration, type, name, person, pureNumber)  \
				values('%s', %d, %d, %d, '%s', %d, '%s')"),
			TABLE_CALL,
			pCall->strNumber.c_str(),
			pCall->callTime,
			pCall->nDuration,
			pCall->nType,
			pCall->strName.c_str(),
			pCall->nContactId,
			pCall->strPureNumber.c_str()
			);

	OpenContactDB();
	BOOL nRet = m_contactDB.execDML(szSQL);
	CloseContactDB();

	return (nRet == 0) ? TRUE : FALSE;
}

BOOL CPimDBHandle::SearchCall(CALL* pCall)
{
	return TRUE;
}

BOOL CPimDBHandle::UpdateCall(CALL* pCall)
{
	return TRUE;
}

BOOL CPimDBHandle::DeleteCall(CALL* pCall)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("delete from %s where _id = %d"),
			TABLE_CALL, pCall->nId);

	OpenContactDB();
	BOOL nRet = m_contactDB.execDML(szSQL);
	CloseContactDB();

	return (nRet == 0) ? TRUE : FALSE;
}


//----------------------------------------------------------------------
//  message operations
//----------------------------------------------------------------------
BOOL CPimDBHandle::LoadMessages(CPimMessageMap& pMsgs)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("select * from sms order by date desc"));

	OpenSmsDB();
	CppSQLite3Query Recordset = m_smsDB.execQuery(szSQL);
	while(!Recordset.eof())
	{
		MESSAGE* pMsg = new MESSAGE;
		pMsg->nId				= Recordset.getIntField(0);
		pMsg->nThreadId			= Recordset.getIntField(1);
		pMsg->nAssociationId	= Recordset.getIntField(2);
		pMsg->nPartNumber		= Recordset.getIntField(3);
		pMsg->strAddress.assign	( Recordset.getStringField(4));
		pMsg->nDate				= Recordset.getIntField(5);
		pMsg->nMsgType			= Recordset.getIntField(6);
		pMsg->nRead				= Recordset.getIntField(7);
		pMsg->nStatus			= Recordset.getIntField(8);
		pMsg->nType				= Recordset.getIntField(9);
		pMsg->strSubject.assign	( Recordset.getStringField(10));
		pMsg->strBody.assign	( Recordset.getStringField(11));

		CPimMessage* pPimMsg = new CPimMessage(pMsg, this);
		if (pMsgs.find(pMsg->nId) == pMsgs.end())
		{
			CPimMessageArray* pMsgArray = new CPimMessageArray;
			pMsgs.insert(make_pair(pMsg->nId, pMsgArray));
		}
		pMsgs[pMsg->nId]->push_back(pPimMsg);
		Recordset.nextRow();
	}
	Recordset.finalize();
	CloseSmsDB();

	return TRUE;
}

BOOL CPimDBHandle::CreateMessage(MESSAGE*	pMsg)
{
	return TRUE;
}

BOOL CPimDBHandle::SearchMessage(MESSAGE* pMsg)
{
	BOOL nRet;
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("select * from sms where _id = %d"), pMsg->nId);

	OpenSmsDB();
	CppSQLite3Query Recordset = m_smsDB.execQuery(szSQL);
	if (!Recordset.eof())
	{
		pMsg->nId				= Recordset.getIntField(0);
		pMsg->nThreadId			= Recordset.getIntField(1);
		pMsg->nAssociationId	= Recordset.getIntField(2);
		pMsg->nPartNumber		= Recordset.getIntField(3);
		pMsg->strAddress.assign	(Recordset.getStringField(4));
		pMsg->nDate				= Recordset.getIntField(5);
		pMsg->nMsgType			= Recordset.getIntField(6);
		pMsg->nRead				= Recordset.getIntField(7);
		pMsg->nStatus			= Recordset.getIntField(8);
		pMsg->nType				= Recordset.getIntField(9);
		pMsg->strSubject.assign	(Recordset.getStringField(10));
		pMsg->strBody.assign	(Recordset.getStringField(11));

		nRet = TRUE;
	}
	else
	{
		nRet = FALSE;
	}
	CloseSmsDB();

	return nRet;
}

BOOL CPimDBHandle::UpdateMessage(MESSAGE* pMsg)
{
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("update %s set read = %d where _id = %d"),
			TABLE_SMS, pMsg->nRead, pMsg->nId);

	OpenSmsDB();
	int nRet = m_smsDB.execDML(szSQL);
	CloseSmsDB();

	return (nRet == 0) ? TRUE : FALSE;
}

BOOL CPimDBHandle::DeleteMessage(MESSAGE* pMsg)
{
	BOOL nRet;
	TCHAR szSQL[256];
	_stprintf_s(szSQL, sizeof(szSQL), _T("delete from %s where _id = %d"),
			TABLE_SMS, pMsg->nId);

	OpenSmsDB();
	CppSQLite3Query Recordset = m_smsDB.execQuery(szSQL);
	nRet = TRUE;

	return nRet;
}



//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
BOOL CPimGoogleHandle::LoadContacts(CPimContactMap& pContactMap)
{
	return TRUE;
}

BOOL CPimGoogleHandle::CreateContact(CONTACT* pContact)
{
	return TRUE;
}

BOOL CPimGoogleHandle::SearchContact(CONTACT* pContact)
{
	return TRUE;
}

BOOL CPimGoogleHandle::UpdateContact(CONTACT* pContact)
{
	return TRUE;
}

BOOL CPimGoogleHandle::DeleteContact(CONTACT* pContact)
{
	return TRUE;
}


BOOL CPimGoogleHandle::LoadMessages(CPimMessageMap& pMsgs)
{
	return TRUE;
}

BOOL CPimGoogleHandle::CreateMessage(MESSAGE* pMsg)
{
	return TRUE;
}

BOOL CPimGoogleHandle::SearchMessage(MESSAGE* pMsg)
{
	return TRUE;
}

BOOL CPimGoogleHandle::UpdateMessage(MESSAGE* pMsg)
{
	return TRUE;
}

BOOL CPimGoogleHandle::DeleteMessage(MESSAGE* pMsg)
{
	return TRUE;
}


//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimPersonalInfo::CPimPersonalInfo()
{
}

CPimPersonalInfo::CPimPersonalInfo(CPimDataHandle* pHandle) :
	m_pHandle(pHandle)
{
}


//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimContact::CPimContact()
{
}

CPimContact::CPimContact(CONTACT* pContact, CPimDataHandle* pHandle) :
	m_pContact(pContact),
	CPimPersonalInfo(pHandle)
{
}

BOOL CPimContact::Update()
{
	m_pHandle->UpdateContact(m_pContact);
	return TRUE;
}

CONTACT* CPimContact::GetContact()
{
	return m_pContact;
}

//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimMessage::CPimMessage()
{
}

CPimMessage::CPimMessage(MESSAGE* pMsg, CPimDataHandle* pHandle) :
	m_pMsg(pMsg),
	CPimPersonalInfo(pHandle)
{
}

BOOL CPimMessage::Update()
{
	m_pHandle->UpdateMessage(m_pMsg);
	return TRUE;
}

MESSAGE* CPimMessage::GetMsg()
{
	return m_pMsg;
}

//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimPersonalInfoSet::CPimPersonalInfoSet(CPimDataHandle* pHandle) :
	m_pHandle(pHandle)
{
}



//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimContacts::CPimContacts(CPimDataHandle* pHandle) :
	CPimPersonalInfoSet(pHandle)
{
}

BOOL CPimContacts::Load()
{
	return m_pHandle->LoadContacts(m_pContacts);
}

BOOL CPimContacts::Clear()
{
	CPimContactMapIter iter = m_pContacts.begin();
	for (; iter != m_pContacts.end();)
	{
		if ((*iter).second != NULL)
			delete (*iter).second;

		m_pContacts.erase(iter++);
	}

	return TRUE;
}

CPimContact* CPimContacts::Search( const CPimString& strPhoneNumber)
{
	CPimContactMapIter iter = m_pContacts.begin();
	for (; iter != m_pContacts.end(); ++iter)
	{
		CONTACT* pPC 	= (*iter).second->GetContact();
		CPimPhoneArray* pPhone	= pPC->pPhoneArray;
		CPimPhoneArrayIter it = pPhone->begin();
		for(; it != pPhone->end(); ++it)
		{
			if (strPhoneNumber.find((*it)->strPureNumber) != -1)
				return (*iter).second;
		}
	}
	return NULL;
}

BOOL CPimContacts::FuzzySearch(CPimStringArray& strFirstLetters,
		CPimContactMap& pContactMap)
{
	BOOL nRet = TRUE;
	CPimIntArray arrContactId;
	m_pHandle->SearchContactsByFirstLatter(strFirstLetters, arrContactId);

	CPimIntArrayIter iterId = arrContactId.begin();
	for (; iterId != arrContactId.end(); ++iterId)
	{
		pContactMap.insert(make_pair(*iterId, m_pContacts[*iterId]));
	}

	return nRet;
}

CPimContactMap& CPimContacts::GetContacts()
{
	return m_pContacts;
}

//----------------------------------------------------------------------
//  
//----------------------------------------------------------------------
CPimMessages::CPimMessages(CPimDataHandle* pHandle) :
	CPimPersonalInfoSet(pHandle)
{
}

BOOL CPimMessages::Load()
{
	return m_pHandle->LoadMessages(m_pMsgs);
}

BOOL CPimMessages::SetMsgMap(CPimContactMap& pContactMap)
{
	//	if (m_pMsgArray != NULL && pContactMap != NULL)
	//	{
	//		CPimMessageArrayIter msgIter	 = m_pMsgArray->begin();
	//		CPimContactMapIter contactIter = pContactMap->begin();
	//		for (; msgIter != m_pMsgArray->end(); ++msgIter)
	//		{
	//			for (; contactIter != m_pContacts.end(); ++contactIter)
	//			{
	//
	//			}
	//	}
	return TRUE;
}

BOOL CPimMessages::Clear()
{
	CPimMessageMapIter iter = m_pMsgs.begin();
	for (; iter != m_pMsgs.end();)
	{
		if ((*iter).second != NULL)
			delete (*iter).second;

		m_pMsgs.erase(iter++);
	}

	return TRUE;
}

CPimMessageMap&	CPimMessages::GetMsgs()
{
	return m_pMsgs;
}

//=====================================================================================
// vim: ft=cpp tabstop=4 shiftwidth=4
